מילון עם מפתחות שלמים Lecture of Geiger & Itai s slide brochure www.cs.technion.ac.il/~dang/courseds טבלאות ערבול הפעולות הבסיסיות של מילון הן כזכור חיפוש, הכנסה, והוצאה. אם המפתחות מספרים שלמים בתחום [,,], ניתן לממש את הפעולות בזמן () במקרה הגרוע, באמצעות שימוש במערך בגודל. ידוע כי שלושת Data Data מימוש הפעולות: (). זמן: במערך. בתא ה- Data כתיבת Insert() (). זמן: במערך. מחיקת המידע בתא ה- Delete() (). ריק. זמן: לא החזר "קיים" אמ"מ התא ה- Find() חומר קריאה לשיעור זה Chapter - Hash tables (pages 9 ) cs, Technion Geiger & Itai, ערבול (Hashing) מילון עם מפתחות שלמים מימוש מילון באמצעות מערך נקרא גישה ישירה Addressing) :(Direct המפתח עצמו משמש כאינדקס במערך. כאשר מרחב המפתחות גדול נחשב את האינדקס מתוך המפתח באמצעות פונקצית ערבול. המטרה לממש את פעולות החיפוש, הכנסה, והוצאה בזמן ממוצע של (). נגדיר פונקצית ערבול (hash),h: {,, } : אשר בהינתן מפתח בתחום מחשבת אינדקס בטווח המתאים. האינדקס של מפתח יהיה.h() = h() = דוגמא:,,, 9,, 9 קלט להכנסה: בשיטת הערבול נוצרות התנגשויות כאשר h().h() = לדוגמא, עבור הפונקציה h אבל h().h() = = הנ"ל: 9 9 9 cs, Technion שלושת הפעולות מתבצעות בזמן ( Θ(log כאשר משתמשים בעץ חיפוש מאוזן או ברשימות דילוגים. מדוע לפיכך משתמשים במבנים אלה? תשובה: ממספר בהרבה גדול המפתחות ערכי של הטווח גודל לעתים המפתחות בהם משתמשים, מה שמניב פתרון בזבזני בזיכרון. Data דוגמא : מספרי תעודת זהות מורכבים מתשע ספרות עשרוניות. כלומר קיימים 9 מפתחות אך בישראל יש פחות מ אנשים. לפיכך שימוש במערך ינצל פחות מ % בודד של הזיכרון המוקצה למערך. Data, באורך עבריות אותיות של המחרוזות מספר : דוגמא באמצעותן ניתן לתאר שם פרטי, שם אמצעי, ושם משפחה של תושבי ישראל, הוא בעוד מספר האנשים קטן מ. cs, Technion
דוגמא פתרון להתנגשויות באמצעות רשימות מקושרות נניח: קלט: 9 9 = h() =,,, 9,,, הערה: = היא. נבחר לצורכי נוחיות ההסבר. נראה בהמשך שבחירה טובה יותר cs, Technion מימוש הפעולות: Insert(, ) Search(, ) Delete(, ) הכנס את בראש הרשימה )].[h(. זמן: () במקרה הגרוע*. חפש איבר עם מפתח ברשימה. h זמן: (אורך הרשימה) Θ במקרה הגרוע. סלק את מהרשימה )].[h(. זמן: (אורך הרשימה) Θ במקרה הגרוע. * הערה: הכנסה ב-( ( אפשרית אם נרשה מופעים חוזרים של אותו מפתח. אחרת, בהכנסה נבצע קודם חיפוש, ולכן גם פעולה זו תיקח (אורך הרשימה) Θ זמן במקרה הגרוע. cs, Technion ניתוח זמנים ניתוח זמנים הוכחה: משפט: בשיטת השרשראות ותחת הנחת הפיזור האחיד הפשוט, זמן חיפוש כושל ממוצע הוא.Θ בהנחת הפיזור האחיד הפשוט כל מפתח מגיע באקראי לאחת מ- הרשימות. הזמן לחיפוש כושל הוא לפיכך הזמן הממוצע לחפש באחת הרשימות עד סופה. אורכה הממוצע של רשימה בהנחת הפיזור האחיד הוא. / לפיכך בממוצע יידרש Θ זמן (הכולל את זמן בדיקת המצביע בסוף הרשימה). ארבעה מצביעים 9 9 cs, Technion במקרה הגרוע ביותר כל האיברים נכנסו לאותה הרשימה ואז זמן חיפוש\הוצאה הוא.Θ() ברור לפיכך שאין משתמשים בערבול בגלל הזמן המקסימלי לפעולה אלא בגלל הזמן הממוצע לפעולה. נרצה לבחור פונקצית ערבול שמפזרת היטב את המפתחות לרשימות השונות. נניח לרגע שהצלחנו, כלומר h מפזרת את המפתחות באופן אחיד. פורמלית: הגדרה: פונקציית כי נאמר ערבול cs, Technion הנחת את מקיימת h הפיזור האחיד הפשוט (/) ממפה איבר אקראי בהסתברות אחידה h אם (simple uniform hash) לכל אחד מהתאים, ובאופן בלתי תלוי בשאר האיברים. ננתח כעת את זמני החיפוש תחת הנחת הפיזור האחיד הפשוט. הגדרה: יהי מספר המפתחות בשימוש ויהי גודל הטבלה. פקטור העומס מוגדר ע"י. = תחת הנחת הפיזור האחיד הפשוט האורך הממוצע של שרשרת הוא, מכיוון שהאיברים מתחלקים בצורה שווה בין השרשראות השונות.
ניתוח זמנים ניתוח זמנים משפט: בשיטת השרשראות ותחת הנחת הפיזור האחיד הפשוט, זמן חיפוש כושל ממוצע הוא ( )Θ. + משפט: בשיטת השרשראות ותחת הנחת הפיזור האחיד הפשוט, זמן חיפוש מוצלח ממוצע הוא (/ )Θ. + מסקנה: כאשר סדר הגודל של מספר המפתחות בהם משתמשים הוא כגודל המערך, כלומר עבור (), = נקבל שגורם העומס קבוע כלומר () = ולכן בשיטת השרשראות, תחת הנחת הפיזור האחיד הפשוט, כל הפעולות דורשות זמן () בממוצע. לדוגמא: עבור מפתחות מטווח כלשהו של מספרים שלמים, נאמר עד, נוכל להחזיק מערך ובו מקומות ובממוצע אורך כל שרשרת יהיה וזמני החיפוש יהיו בהתאם. cs, Technion הוכחה: משפט: בשיטת השרשראות ותחת הנחת הפיזור האחיד הפשוט, זמן חיפוש מוצלח ממוצע הוא.Θ / יהיו,, המפתחות בטבלה בזמן החיפוש, לפי סדר הכנסתם. מהו זמן חיפוש הממוצע של המפתח? אחרי מפתח זה נוספו מפתחות נוספים. לכן בממוצע גודל הרשימה משמאל למפתח הוא. מכאן שזמן החיפוש הממוצע של המפתח הוא. זמן החיפוש הממוצע למפתח כלשהו יהיה לפיכך: 9 אחרי i לפני המפתח 9 cs, Technion 9 9 סריקה לינארית Probing Linear בסריקה ליניארית, בעת הכנסה, אם המקום המיועד h() תפוס, נסה במקום הבא מודולו (וכך הלאה). = h() = דוגמא:,,, 9,,, קלט: 9 חיפוש מתבצע באותה שיטה. פתרון ללא שרשראות להתנגשויות Open Addressing בשיטת Open addressing לא נשתמש בשרשראות. במקום זאת, כל האיברים יוכנסו לטבלה. ברור כי בשיטה זו פקטור העומס קטן\שווה אחד ( ). כיוון שלא נפתור את בעיית ההתנגשויות באמצעות רשימות, נצטרך להציע פתרון אחר להתנגשויות. נבחן שלושה פתרונות להתנגשויות:. סריקה ליניארית.. ערבול נשנה.. ערבול כפול. cs, Technion cs, Technion
סריקה לינארית - יתרונות וחסרונות בשיטת הוצאה Open Addressing היתרון העיקרי של סריקה ליניארית הוא פשטות. אבל סריקה ליניארית נוטה למלא בלוקים מכיוון שכאשר קיים בלוק, האיברים הבאים מצטרפים אליו בסבירות גבוהה יותר מאשר הסבירות למלא תא חדש בודד. לפיכך, סריקה ליניארית אינה מהווה קרוב טוב להנחת הפיזור האחיד. כאשר השימוש דורש הוצאות, אורך החיפוש תלוי גם באיברים שכבר הוצאו ולא רק באיברים שכרגע במבנה. כיצד נוציא איבר? לא ניתן פשוט למחוק איבר שכן שרשרת החיפוש תינתק עבור איברים אחרים. פתרון: נסמן את מקום האיבר שהוצא בסימן.delete בזמן חיפוש : במידה וניתקל בסימן,delete נמשיך את סריקת הרשימה עד למציאת או עד להגעה למקום ריק (המסומן ב-.(Null בזמן הכנסת : במידה וניתקל בסימן,delete נשתמש במקום זה לשמירת. דוגמאות לשימוש במילון ללא הוצאות: טבלה של שמות משתנים בהרצת תוכנית Table).(Symbol מספרי תעודות זהות אינם ממוחזרים. נתאר כעת שיטות נוספות ל- open addressing שמדמות טוב יותר את הנחת הפיזור האחיד הפשוט. שיטות אלו שימושיות במיוחד במימושי מילון ללא הוצאות. כאשר יש צורך בהוצאות, עדיפה שיטת הרשימות המקושרות. cs, Technion דוגמא: קלט: הוצא, חפש delete = h() =,,, 9,,,, הכנס cs, Technion 9 9 ערבול כפול - Hashing Double ערבול נשנה - Rehashing נגיע לתוצאות דומות לערבול נשנה ע"י שתי פונקציות בלבד,. h כאשר: h () = h + () הפונקציות, h נבחרות באופן בלתי תלוי. נניח שברשותנו סדרה אינסופית של פונקציות ערבול:, h, h, h ננסה לשמור את במקום () h. אם תפוס, ננסה במקום () h. נמשיך עד שנצליח. לדוגמא: בסריקה ליניארית מתקיים.h () = h() + מהו זמן ההכנסה הממוצע? תחת הנחת הפיזור האחיד הפשוט ההסתברות שמקום תפוס היא. היא תפוס המקום הראשונים הניסיונות שב- ההסתברות שפונקציות הערבול בלתי תלויות). לכן ההסתברות שאורך החיפוש בדיוק היא ). ( מכאן שאורך החיפוש הממוצע, תחת הנחת הפיזור האחיד הוא: לדוגמא עבור (בהנחה מהו היחס בין () לגודל הטבלה,? גודל הטבלה ו- () צריכים להיות מספרים זרים כך ש () h,(), h יכסו את כל האינדקסים האפשריים בתחום {,,}. לפיכך נוח לבחור את להיות מספר ראשוני. הערה: בשלושת הדוגמאות שראינו לערבול בשיטת Open Addressing (סריקה לינארית, ערבול נשנה וערבול כפול), ההוצאות נעשות ע"י שימוש בסימון.delete = = = / אורך החיפוש הממוצע הוא. cs, Technion cs, Technion
פונקציות ערבול פונקציות ערבול דרישות מפונקציות ערבול: מפזרת היטב וקלה לחישוב. בשקפים הבאים נציג מספר פונקציות ערבול קלות לחישוב, יחד עם דרישות מהן, כדי שידמו היטב את הנחת הפיזור האחיד הפשוט. הערה: רצוי לבדוק את פונקצית הערבול על תת קבוצה של מפתחות "אמיתיים" וכך לוודא שהנחת הפיזור האחיד מתקיימת בקרוב. שיטת החילוק מודולו : h() = ( ) עבור פונקצית ערבול זו, רצוי ש- : לא יהיה חזקה של או. בחזקות של פונקצית הערבול מסתמכת רק על () log הביטים הראשונים.(LSB) בחזקות של עשר, פונקצית הערבול מסתמכת רק על () log הספרות הראשונות. רצוי שפונקציות הערבול ישתמשו בכל האינפורמציה הנמצאת במפתח כדי לקרב עד כמה שניתן את הנחת הפיזור האחיד. יהיה ראשוני שאינו קרוב לחזקה של. חזקות קרובות של גורמות לפיזור לא אחיד כאשר המפתחות כתובים בבסיס שהוא חזקה של, למשל מחרוזות תווים נכתבות בבסיס =. cs, Technion cs, Technion פונקציות ערבול למחרוזות ארוכות פונקציות ערבול כדי לדון במחרוזת כמספרים, נשתמש בקוד וכך הלאה = 9 = :ascii = 9 = פתרון נאיבי: בצע xor ביט ביט. לדוגמא: = ) ( = )) ( h( ) = h(( ) חסרון ראשון: h( ) = h(( ) ( )) = ( ) = h( ) = h(( ) ( )) = ( ) = = שיטת הכפל עבור קבוע < <. הכפל את המפתח בקבוע... מצא את החלק השבור של התוצאה. הכפל את החלק השבור ב- ועגל כלפי מטה: הערך של אינו קריטי. ערך של הגורם לפיזור טוב הוא : / =. h = ( ) דוגמא: =.( = ) =, התוצאה אפס מתקבלת כאשר כל אות מופיעה מספר זוגי של פעמים. למשל: = h חסרון שני: טווח הערכים מוגבל: h. cs, Technion h = (. ) = (. ) =. =. = cs, Technion 9
פונקציות ערבול למחרוזות ארוכות פונקציות ערבול למחרוזות ארוכות מימוש של פונקצית הערבול מהשקף הקודם בשפת :C/C++ int hash(char *s) { int h = ; char *p; for (p=s; *p; p++) h = T[h]^ *p; /* Xor */ return h; } פתרון עדיף: (על עקרון השפעת הפרפר מגינאה על מזג האוויר שינוי קטן מביא ) (π,, π של ואחסן אותה שינוי גדול) בחר פרמוטציה אקראית במערך. פונקצית הערבול: עבור המפתח. = = []. בשלב ה- :. [ ] תוצאת פונקצית הערבול:. hh = cs, Technion דוגמא: hh() hh() = [] 9 = = = hh() = [hh()] = [] 9 = = הערה: בשיטה זו נפתרה בעיית האותיות המופיעות מספר זוגי של פעמים. cs, Technion ערבול אוניברסלי פונקציות ערבול למחרוזות ארוכות לכל בחירה של פונקצית ערבול קיימת סדרה גרועה של מפתחות כך שתיווצר רשימה באורך מקסימלי. תכונה זו יכולה ליצור בעיה. לדוגמא: יתכן מתכנת המשתמש באופן עקבי בשמות מסוימים למשתני התוכניות שהוא כותב ולצערו פונקצית הערבול הבונה את ה- table symbol ממפה את כל השמות לאותו הנ"ל המקום בטבלת משתמש זה אינה יעילה כפי שיכולה הייתה הערבול. להיות! פתרון לבעית הטווח של הפונקציה מהעמוד הקודם: כדי להתגבר על בעיית הטווח ניתן להשתמש בשתי פרמוטציות, ולשרשר את התוצאות: hh() = [hh (), hh ()] = hh () + hh () גודל הטווח החדש, כלומר גודל טבלת הערבול, הוא =. (בעוד גודל כל תוכנית כל לפיכך של מחשב הוא ). ניתן להגיע לטווח הרצוי ע"י שימוש במספר קטן של פרמוטציות נוספות. הפתרון: לבחור באקראי, בזמן יצירת טבלת ערבול, פונקצית ערבול מתוך קבוצת פונקציות שהוגדרה מראש. בכדי שגישה זו תועיל לנו, נרצה שקבוצת הפונקציות תהיה כזו, שעבור כל סדרת מפתחות, בחירה אקראית של אחת הפונקציות תיצור פיזור טוב (הגדרה בעמוד הבא). cs, Technion תוצאה דומה מתקבלת אם במקום hh נוסיף לאות הראשונה של המחרוזת ונשתמש שוב בפונקצית הערבול.hh לדוגמא: () hh() = hh () + hh cs, Technion
ערבול אוניברסלי ערבול אוניברסלי משפט: תהי קבוצה אוניברסלית של פונקציות ערבול לתוך טבלה בגודל. אם h נבחרה באקראי מתוך ונשתמש בה לערבול מפתחות כלשהם, אזי לכל. מפתח, המספר הצפוי של התנגשויות בשיטת הרשימות המקושרות שווה ל-.,,, מספר הגדרה: תהי קבוצת פונקציות ערבול מהתחום לקבוצה הקבוצה נקראת אוניברסלית אם לכל זוג מפתחות שונים הפונקציות ב- עבורן h() h() = הוא. / היא מסוים מפתח עם מפתח מסוים של להתנגשות הוכחה: ראינו שההסתברות. = / המספר הצפוי של התנגשויות של מפתח מסוים עם מפתח כלשהו נתון לפיכך לסכום שווה אקראיים משתנים סכום של שממוצע בעובדה שימוש (תוך ע"י הממוצעים): = : = = שבבחירה אקראית של פונקצית ערבול מתוך, מפתח הבחנה: ההסתברות יתנגש עם מפתח אחר היא = / = / נראה כעת ששימוש בקבוצה אוניברסלית גורם לפיזור טוב. אח"כ נראה כיצד לבנות קבוצה כזו. מסקנה: מספר ההתנגשויות הצפוי לכל מפתח קטן מגורם העומס. cs, Technion cs, Technion בניית קבוצה אוניברסלית בניית קבוצה אוניברסלית משפט: קבוצת הפונקציות } = h} מהשקף הקודם היא קבוצה אוניברסלית. הוכחה: יהיו ] = [,, ו- ] = [,, מפתחות שונים. בלי הגבלת הכלליות נניח כי. בפרט, ). ( טענה: לכל ערך קבוע של,, קיים יחיד כך שמתקיים ().h () = h הערך מתקבל מהפתרון היחיד למשוואה: h h = ( ) הניתנת לשכתוב כדלקמן: ( ) נוכיח את הטענה בשקף הבא. בהנחה שהטענה נכונה, נובע שכל זוג מפתחות ( קיים,, ) וזאת כיוון שלכל ערך של מתנגשים עבור ערכים של, ערך אחד עבורו, מתנגשים. נזכר שמספר הפונקציות ב- הוא כמספר ערכי, כלומר. = לכן מתקיים כי מספר הפונקציות עבורן מתנגשות הוא, = / כנדרש מקבוצה אוניברסלית. cs, Technion נבחר את גודל הטבלה להיות מספר ראשוני. נשבור כל מפתח ל- + חלקים באורך קבוע ]. = [,, (למשל באורך בית = ביטים), כך שמספר הביטים של יהיה פחות ממספר הביטים הנחוץ לייצוג גודל הטבלה,. לכל סדרה ] = [,, מהתחום,.., נגדיר פונקצית ערבול () h בצורה הבאה: h = קבוצת הפונקציות היא } {h ומספר הפונקציות בה הוא. דרך השימוש בשיטה זו: כאשר משתמש מגדיר טבלת ערבול בגודל, התוכנית מגרילה מספר ומשתמשת בפונקצית הערבול h לכל הפעולות הנעשות בטבלת ערבול זו. דוגמא: =, טווח המפתחות( (,.,, נשבור כל מפתח לשלושה חלקים באורך ביטים. נניח שהוגרלו המספרים [,,] =. בהינתן המפתח [,,] = = נחשב את מקומו בטבלת הערבול ע"י הנוסחה: ( + + ) = 9 cs, Technion...
מגבלות לערבול בניית קבוצה אוניברסלית בעיה: בכדי להבטיח כי () = ולכן פקטור העומס הוא, = = צריך לדעת מראש סדר גודל למספר האיברים שמתעתדים להכניס למבנה (). פתרון חלקי: כאשר טבלת ערבול מתמלא ניתן להקצות טבלה חדשה בגודל כפול, להכניס את כל האיברים לטבלה החדשה, ולהיפטר מהטבלה הישנה. הזמן המשוערך הממוצע יהיה () למרות שמדי פעם תתבצע פעולה יקרה. עוד על פתרון זה בתרגולים. cs, Technion טענה: למשוואה הבאה כפונקציה של הוכחת הטענה: נזכר שכאשר ראשוני מתקיים: לכל מספר ( ( קיים מספר יחיד כך ש- ). ( המספר נקרא ההוכפי של. למשל: ) ( יש פתרון והפתרון יחיד. ( ) במשוואה הנתונה מתקיים, = נכפיל את המשוואה בהופכי של ונקבל את הפתרון היחיד ל- : ( ) יחידות הפתרון: יהיו ו- פתרונות למשוואה זו. אזי ). ( נכפול בהופכי של ) = ( ונקבל כי ). ( cs, Technion 9 ניתוח זמנים עבור open addressing הנחת הפיזור האחיד: הסדרה ) (h, h, h, h היא פרמוטציה אקראית של ),.(, שימוש: בעיית היחידות Element Uniqueness קלט: מספרים שלמים <. open addressing מתקיים: הבעיה: מצא האם קיימים עבורם. = משפט: בהנחת הפיזור האחיד, בשיטת ערבול זמן ממוצע של חיפוש כושל קטן מ- ( )/. זמן ממוצע של חיפוש מוצלח קטן מ-. ln + פתרון ראשון מיון: אחרי המיון נבדוק האם קיימים איברים סמוכים שווים. זמן: ) ( log הוכחה בספר הלימוד: "Introduction to algorithms, Cormen et al., pp -9 פתרון שני ערבול: הכנס את המספרים לטבלת ערבול בגודל () (שיתכן וקטנה בהרבה מ- ). בזמן התנגשות, בדוק שוויון. זמן: () בממוצע. הערה: המשפט תאורטי שכן קשה לקיים את הנחת הפיזור האחיד. cs, Technion cs, Technion
סיכום השיעור ניתוח זמנים עבור סריקה לינארית בשיעור זה ראינו:.. טבלת ערבול מימוש נוסף של מילון, בעל זמן ריצה () בממוצע ל- פעולות המילון. מימושים שונים פונקצית הערבול: טבלת של ערבול ( (. משפט: בהנחת הפיזור האחיד הפשוט, בשיטת ערבול open addressing בסריקה ליניארית מתקיים: פתרון באמצעות רשימות מקושרות מספר שיטות :Open Addressing סריקה לינארית ערבול נשנה ערבול כפול. הטיפול אופן כולל פונקציות ערבול שונות ביניהן דוגמה לקבוצה אוניברסלית. בהתנגשויות של זמן ממוצע של חיפוש /() כושל קטן מ- זמן ממוצע של חיפוש מוצלח קטן מ- הוכחה בספר: / Knuth, The Art of Computer Programming, Vol, 9 הערה: המשפט מראה שסריקה ליניארית אפשרית לשימוש. את הנחת הפיזור האחיד הפשוט קל יותר לקרב מאשר את הנחת הפיזור האחיד. cs, Technion cs, Technion cs, Technion